#include <cjson/cJSON.h>
#include "config/CustomConfig.h"
#include <stdlib.h>

// cJSON第三方库安装(openssl@3类似)：
// 由于cJSON不像SDL2和SDL_mixer那样在find_package时返回目标target，因此在CMakeList.txt引入时需要额外处理：
// 1️⃣包管理器Homebrew安装cJSON第三方库：brew install cjson
// 2️⃣ ✅由于Homebrew安装第三方库存储路径在不同系统中可能不同，因此路径不能写死，此处先自动检测路径，
// OUTPUT_VARIABLE定义结果变量CJSON_PREFIX：
// execute_process(COMMAND brew --prefix cjson OUTPUT_VARIABLE CJSON_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE)

// 3️⃣步骤2️⃣获取到的头文件路径CJSON_PREFIX，告诉编译器头文件的路径：
// include_directories(
//     ${CJSON_PREFIX}
// )

// 4️⃣链接cJSON第三方库的库文件dylib：target_link_libraries(main PUBLIC ${CJSON_PREFIX}/lib/libcjson.dylib)
// ✅即非现代CMake / 无法通过find_package找到目标target的情况，采用手动做include_directories() + target_link_libraries()的方式：
// 1、execute_process(COMMAND brew --prefix cjson OUTPUT_VARIABLE CJSON_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE)
// 2、include_directories(${CJSON_PREFIX})
// 3、target_link_libraries(main PUBLIC ${CJSON_PREFIX}/lib/libcjson.dylib)



// MARK: ------------------------------------- char *字符数组，字符串

/**
 * 获取cJSON对象的字符串字段的值
 * @param json	cJSON对象
 * @param key	字段名称
 * @param nullAble	返回值是否可以为NULL
 * @return 指向字符数组的指针，类型为char *，在C中用作字符串，必须以\0字符结尾，\0字符占字符数组1个长度。
 */
char *analysisCJSONString(const cJSON *const json, const char *const key, _Bool nullAble) {
    cJSON *keyJson = cJSON_GetObjectItemCaseSensitive(json, key);
    if (cJSON_IsString(keyJson) && keyJson->valuestring)
    {
        return keyJson->valuestring;
    }
    if (nullAble)
    {
        return NULL;
    } else {
        return "";
    }

}

/**
 * 获取cJSON对象的字符串字段的值
 * @param json	cJSON对象
 * @param key	字段名称
 * @return 指向字符数组的指针，类型为char *，在C中用作字符串，必须以\0字符结尾，\0字符占字符数组1个长度。
 */
char *getCJSONString(const cJSON *const json, const char *const key) {
    //报错❌call to undeclared function 'analysisCJSONString'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    //✅原因分析：1️⃣analysisCJSONString方法没有写在被调用处的前面，导致编译器找不到该方法
    // 2️⃣也可能是因为没有include头文件
    //✅解决办法：1️⃣确保方法声明在该方法被调用处的前面；2️⃣确保调用处所在文件有include该function所在的头文件；
    return analysisCJSONString(json, key, BOOL_FALSE);//默认返回值不能为NULL
}


// MARK: ------------------------------------- char *字符数组，字符串

/**
 * 获取cJSON对象的int字段的数字值
 * @param json	cJSON对象
 * @param key	字段名称
 * @return 数字值，整型数值
 */
int getCJSONInt(const cJSON *const json, const char *const key) {
    const cJSON *intJson = cJSON_GetObjectItemCaseSensitive(json, key);
    if (cJSON_IsNumber(intJson))
        return intJson->valueint;

    return 0;// 没找到或不是数字，返回 0
}

/**
 * cjson对象序列化成字符串，cjson转string转字符串
 * @param json cJSON对象
 * @return json string字符串
 */
char *json_to_string(cJSON *json) {
    if (!json) return "";
    // 序列化为字符串
    char *json_str = cJSON_PrintUnformatted(json); // 或者 cJSON_Print(json) 美化输出
    return json_str;
}

/**
 * 字符串转cjson对象，string转cjson
 * @param json json string字符串
 * @return cJSON对象
 */
cJSON *string_parse_to_jsno(char *json_string) {
    if (!json_string) return NULL;
    cJSON *json = cJSON_Parse(json_string);

    // ❌编译报错：
    // main(39161,0x20055c200) malloc: *** error for object 0x1002f6886: pointer being freed was not allocated
    // main(39161,0x20055c200) malloc: *** set a breakpoint in malloc_error_break to debug

    // ✅原因分析：传入的字符数组对象是一个字符串常量（String Literal）或字面量，例如：char *json_str = "{\"code\": \"984c2\"}"
    // 它在只读内存区（.rodata 段），不是通过malloc()分配的，因此调用free释放内存等于是释放未分配的内存，导致报错
    // free(json_string);  // 释放 json_to_string 分配的内存
    // ✅解决办法：因此对于传入的参数对象，则由调用方根据是否有分配内存来决定是否free释放内存

    return json;
}
